package cn.com.anypay.manager.service;

import cn.com.anypay.manager.common.exception.WeChatAuthException;
import cn.com.anypay.manager.dto.config.WeChatAuthConfig;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.*;

import org.springframework.stereotype.Service;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.ResourceAccessException;
import org.springframework.web.client.RestTemplate;

import java.net.SocketTimeoutException;
import java.util.concurrent.TimeoutException;

/**
 * 微信API网络调用客户端
 * 负责处理与微信开放平台的HTTP通信，包含重试机制和错误处理
 */
@Slf4j
@Service
public class WeChatApiClient {
    
    private final RestTemplate weChatRestTemplate;
    private final WeChatAuthConfig weChatAuthConfig;
    private final WeChatApiMonitorService monitorService;
    
    public WeChatApiClient(@Qualifier("weChatRestTemplate") RestTemplate weChatRestTemplate,
                          WeChatAuthConfig weChatAuthConfig,
                          WeChatApiMonitorService monitorService) {
        this.weChatRestTemplate = weChatRestTemplate;
        this.weChatAuthConfig = weChatAuthConfig;
        this.monitorService = monitorService;
    }
    
    /**
     * 执行GET请求到微信API
     * 
     * @param url 请求URL
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @return API响应
     * @throws WeChatAuthException 当请求失败时抛出异常
     */
    public <T> ResponseEntity<T> executeGet(String url, Class<T> responseType) {
        String apiName = extractApiName(url);
        String requestId = generateRequestId();
        String callId = monitorService.recordApiCallStart(apiName, requestId);
        
        return executeWithRetry(() -> doExecuteGet(url, responseType, callId, apiName), callId, apiName);
    }
    
    /**
     * 实际执行GET请求的方法
     */
    private <T> ResponseEntity<T> doExecuteGet(String url, Class<T> responseType, String callId, String apiName) {
        long startTime = System.currentTimeMillis();
        
        log.debug("执行微信API GET请求: {}", sanitizeUrl(url));
        
        try {
            // 设置请求头
            HttpHeaders headers = createDefaultHeaders();
            HttpEntity<String> entity = new HttpEntity<>(headers);
            
            // 执行请求
            ResponseEntity<T> response = weChatRestTemplate.exchange(
                    url, HttpMethod.GET, entity, responseType);
            
            // 验证响应
            validateResponse(response);
            
            // 记录成功调用
            long duration = System.currentTimeMillis() - startTime;
            long responseSize = estimateResponseSize(response);
            monitorService.recordApiCallSuccess(callId, apiName, duration, responseSize);
            
            return response;
            
        } catch (HttpClientErrorException e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API客户端错误: 状态码={}, 响应体={}", e.getStatusCode(), e.getResponseBodyAsString());
            
            WeChatAuthException exception = handleHttpClientError(e);
            monitorService.recordApiCallFailure(callId, apiName, duration, 
                    exception.getWeChatErrorCode(), exception.getWeChatErrorMsg());
            throw exception;
        } catch (HttpServerErrorException e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API服务器错误: 状态码={}, 响应体={}", e.getStatusCode(), e.getResponseBodyAsString());
            
            WeChatAuthException exception = handleHttpServerError(e);
            monitorService.recordApiCallFailure(callId, apiName, duration, 
                    exception.getWeChatErrorCode(), exception.getWeChatErrorMsg());
            throw exception;
        } catch (ResourceAccessException e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API网络访问错误", e);
            
            WeChatAuthException exception = WeChatAuthException.networkError(e);
            monitorService.recordApiCallFailure(callId, apiName, duration, "50001", "网络连接错误");
            throw exception;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API请求发生未知错误", e);
            
            WeChatAuthException exception = WeChatAuthException.fromWeChatError("50099", "API请求失败: " + e.getMessage());
            monitorService.recordApiCallFailure(callId, apiName, duration, "50099", e.getMessage());
            throw exception;
        }
    }
    
    /**
     * 执行POST请求到微信API
     * 
     * @param url 请求URL
     * @param requestBody 请求体
     * @param responseType 响应类型
     * @param <T> 响应类型泛型
     * @param <R> 请求体类型泛型
     * @return API响应
     * @throws WeChatAuthException 当请求失败时抛出异常
     */
    public <T, R> ResponseEntity<T> executePost(String url, R requestBody, Class<T> responseType) {
        String apiName = extractApiName(url);
        String requestId = generateRequestId();
        String callId = monitorService.recordApiCallStart(apiName, requestId);
        
        return executeWithRetry(() -> doExecutePost(url, requestBody, responseType, callId, apiName), callId, apiName);
    }
    
    /**
     * 实际执行POST请求的方法
     */
    private <T, R> ResponseEntity<T> doExecutePost(String url, R requestBody, Class<T> responseType, String callId, String apiName) {
        long startTime = System.currentTimeMillis();
        
        log.debug("执行微信API POST请求: {}", sanitizeUrl(url));
        
        try {
            // 设置请求头
            HttpHeaders headers = createDefaultHeaders();
            HttpEntity<R> entity = new HttpEntity<>(requestBody, headers);
            
            // 执行请求
            ResponseEntity<T> response = weChatRestTemplate.exchange(
                    url, HttpMethod.POST, entity, responseType);
            
            // 验证响应
            validateResponse(response);
            
            // 记录成功调用
            long duration = System.currentTimeMillis() - startTime;
            long responseSize = estimateResponseSize(response);
            monitorService.recordApiCallSuccess(callId, apiName, duration, responseSize);
            
            return response;
            
        } catch (HttpClientErrorException e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API客户端错误: 状态码={}, 响应体={}", e.getStatusCode(), e.getResponseBodyAsString());
            
            WeChatAuthException exception = handleHttpClientError(e);
            monitorService.recordApiCallFailure(callId, apiName, duration, 
                    exception.getWeChatErrorCode(), exception.getWeChatErrorMsg());
            throw exception;
        } catch (HttpServerErrorException e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API服务器错误: 状态码={}, 响应体={}", e.getStatusCode(), e.getResponseBodyAsString());
            
            WeChatAuthException exception = handleHttpServerError(e);
            monitorService.recordApiCallFailure(callId, apiName, duration, 
                    exception.getWeChatErrorCode(), exception.getWeChatErrorMsg());
            throw exception;
        } catch (ResourceAccessException e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API网络访问错误", e);
            
            WeChatAuthException exception = WeChatAuthException.networkError(e);
            monitorService.recordApiCallFailure(callId, apiName, duration, "50001", "网络连接错误");
            throw exception;
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            log.error("微信API请求发生未知错误", e);
            
            WeChatAuthException exception = WeChatAuthException.fromWeChatError("50099", "API请求失败: " + e.getMessage());
            monitorService.recordApiCallFailure(callId, apiName, duration, "50099", e.getMessage());
            throw exception;
        }
    }
    
    /**
     * 执行带重试逻辑的请求
     * 
     * @param operation 要执行的操作
     * @param callId 调用ID
     * @param apiName API名称
     * @param <T> 返回类型
     * @return 操作结果
     * @throws WeChatAuthException 当所有重试都失败时抛出异常
     */
    private <T> T executeWithRetry(RetryableOperation<T> operation, String callId, String apiName) {
        int maxAttempts = weChatAuthConfig.getRetryCount() + 1; // 包括初始尝试
        Exception lastException = null;
        
        for (int attempt = 1; attempt <= maxAttempts; attempt++) {
            try {
                return operation.execute();
            } catch (ResourceAccessException | HttpServerErrorException e) {
                lastException = e;
                
                if (attempt < maxAttempts) {
                    long delay = calculateRetryDelay(attempt);
                    log.warn("微信API调用失败，第{}次重试，{}ms后重试: {}", attempt, delay, e.getMessage());
                    monitorService.recordApiCallRetry(callId, apiName, attempt, e.getMessage());
                    
                    try {
                        Thread.sleep(delay);
                    } catch (InterruptedException ie) {
                        Thread.currentThread().interrupt();
                        throw WeChatAuthException.fromWeChatError("50099", "重试被中断");
                    }
                } else {
                    log.error("微信API请求重试失败，已达到最大重试次数: {}", maxAttempts - 1, e);
                }
            } catch (WeChatAuthException e) {
                // 对于微信认证异常，直接抛出
                throw e;
            } catch (Exception e) {
                // 对于其他非可重试异常，包装后抛出
                throw WeChatAuthException.fromWeChatError("50099", "API请求失败: " + e.getMessage());
            }
        }
        
        // 所有重试都失败了
        if (lastException instanceof ResourceAccessException) {
            throw WeChatAuthException.networkError(lastException);
        } else if (lastException instanceof HttpServerErrorException) {
            throw WeChatAuthException.fromWeChatError("50001", "微信服务器错误，请稍后重试");
        } else {
            throw WeChatAuthException.fromWeChatError("50099", "API请求重试失败: " + 
                    (lastException != null ? lastException.getMessage() : "未知错误"));
        }
    }
    
    /**
     * 计算重试延迟时间（指数退避）
     * 
     * @param attempt 当前重试次数
     * @return 延迟时间（毫秒）
     */
    private long calculateRetryDelay(int attempt) {
        long baseDelay = 1000; // 基础延迟1秒
        long maxDelay = 10000; // 最大延迟10秒
        long delay = baseDelay * (long) Math.pow(2, attempt - 1);
        return Math.min(delay, maxDelay);
    }
    
    /**
     * 可重试操作的函数式接口
     */
    @FunctionalInterface
    private interface RetryableOperation<T> {
        T execute() throws Exception;
    }
    
    /**
     * 创建默认的HTTP请求头
     * 
     * @return HTTP请求头
     */
    private HttpHeaders createDefaultHeaders() {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("User-Agent", "WeChatAuth/1.0");
        headers.set("Accept", MediaType.APPLICATION_JSON_VALUE);
        headers.set("Accept-Charset", "UTF-8");
        return headers;
    }
    
    /**
     * 验证HTTP响应
     * 
     * @param response HTTP响应
     * @param <T> 响应类型泛型
     * @throws WeChatAuthException 当响应无效时抛出异常
     */
    private <T> void validateResponse(ResponseEntity<T> response) {
        if (response == null) {
            throw WeChatAuthException.fromWeChatError("50001", "收到空响应");
        }
        
        if (!response.getStatusCode().is2xxSuccessful()) {
            throw WeChatAuthException.fromWeChatError("50001", 
                    "HTTP响应状态异常: " + response.getStatusCode());
        }
        
        if (response.getBody() == null) {
            throw WeChatAuthException.fromWeChatError("50001", "响应体为空");
        }
    }
    
    /**
     * 处理HTTP客户端错误（4xx）
     * 
     * @param e HTTP客户端异常
     * @return 微信认证异常
     */
    private WeChatAuthException handleHttpClientError(HttpClientErrorException e) {
        HttpStatus statusCode = HttpStatus.valueOf(e.getStatusCode().value());
        String responseBody = e.getResponseBodyAsString();
        
        switch (statusCode) {
            case BAD_REQUEST:
                return WeChatAuthException.fromWeChatError("40001", "请求参数错误: " + responseBody);
            case UNAUTHORIZED:
                return WeChatAuthException.fromWeChatError("40001", "访问令牌无效或已过期");
            case FORBIDDEN:
                return WeChatAuthException.fromWeChatError("40013", "应用权限不足");
            case NOT_FOUND:
                return WeChatAuthException.fromWeChatError("40004", "请求的资源不存在");
            case TOO_MANY_REQUESTS:
                return WeChatAuthException.apiRateLimit();
            default:
                return WeChatAuthException.fromWeChatError("40000", 
                        "客户端请求错误: " + statusCode + " - " + responseBody);
        }
    }
    
    /**
     * 处理HTTP服务器错误（5xx）
     * 
     * @param e HTTP服务器异常
     * @return 微信认证异常
     */
    private WeChatAuthException handleHttpServerError(HttpServerErrorException e) {
        HttpStatus statusCode = HttpStatus.valueOf(e.getStatusCode().value());
        String responseBody = e.getResponseBodyAsString();
        
        switch (statusCode) {
            case INTERNAL_SERVER_ERROR:
                return WeChatAuthException.fromWeChatError("50001", "微信服务器内部错误");
            case BAD_GATEWAY:
                return WeChatAuthException.fromWeChatError("50002", "微信服务器网关错误");
            case SERVICE_UNAVAILABLE:
                return WeChatAuthException.fromWeChatError("50003", "微信服务暂时不可用");
            case GATEWAY_TIMEOUT:
                return WeChatAuthException.fromWeChatError("50004", "微信服务器响应超时");
            default:
                return WeChatAuthException.fromWeChatError("50000", 
                        "服务器错误: " + statusCode + " - " + responseBody);
        }
    }
    
    /**
     * 对URL进行脱敏处理
     * 
     * @param url 原始URL
     * @return 脱敏后的URL
     */
    private String sanitizeUrl(String url) {
        if (url == null) {
            return null;
        }
        
        // 隐藏敏感参数
        return url.replaceAll("access_token=[^&]*", "access_token=***")
                 .replaceAll("secret=[^&]*", "secret=***")
                 .replaceAll("refresh_token=[^&]*", "refresh_token=***")
                 .replaceAll("code=([^&]{10})[^&]*", "code=$1***");
    }
    
    /**
     * 检查网络连接状态
     * 
     * @return 是否可以连接到微信API
     */
    public boolean checkConnectivity() {
        try {
            // 尝试访问微信API的健康检查端点（如果有的话）
            // 这里可以实现一个简单的连通性检查
            log.debug("检查微信API连通性");
            return true;
        } catch (Exception e) {
            log.warn("微信API连通性检查失败", e);
            return false;
        }
    }
    
    /**
     * 获取当前配置的超时时间
     * 
     * @return 超时时间（毫秒）
     */
    public int getTimeout() {
        return weChatAuthConfig.getTimeout();
    }
    
    /**
     * 获取当前配置的重试次数
     * 
     * @return 重试次数
     */
    public int getRetryCount() {
        return weChatAuthConfig.getRetryCount();
    }
    
    /**
     * 从URL中提取API名称
     * 
     * @param url 请求URL
     * @return API名称
     */
    private String extractApiName(String url) {
        if (url == null) {
            return "unknown";
        }
        
        if (url.contains("oauth2/access_token")) {
            return "getAccessToken";
        } else if (url.contains("sns/userinfo")) {
            return "getUserInfo";
        } else if (url.contains("oauth2/refresh_token")) {
            return "refreshToken";
        } else {
            return "unknown";
        }
    }
    
    /**
     * 生成请求ID
     * 
     * @return 请求ID
     */
    private String generateRequestId() {
        return String.valueOf(System.currentTimeMillis());
    }
    
    /**
     * 估算响应大小
     * 
     * @param response HTTP响应
     * @param <T> 响应类型泛型
     * @return 估算的响应大小（字节）
     */
    private <T> long estimateResponseSize(ResponseEntity<T> response) {
        if (response == null || response.getBody() == null) {
            return 0;
        }
        
        // 简单估算，实际项目中可以更精确地计算
        String bodyString = response.getBody().toString();
        return bodyString.getBytes().length;
    }
}